import numpy as np
import geatpy as ea
"""
派单问题类
将派单问题转换为搜索订单在高维空间的最佳位置
高维空间包含如下维度：
1.时间维度（手艺人的开放时间 + 订单起止时间）
2.候选手艺人维度
3.地理位置维度（手艺人的路程时间）

将手艺人开放时间与时间窗口条件硬塞到简单范围圈定的决策变量中比较困难
geatpy设计的问题框架不利于解决此类问题
"""


class Dispatch(ea.Problem): # 继承Problem父类
    def __init__(self, M):
        name = 'dispatch' # 初始化name（函数名称，可以随意设置）
        maxormins = [1] * M # 初始化maxormins（目标最小最大化标记列表，1：最小化该目标；-1：最大化该目标）
        descition_num = 3   # 决策变量为 时间 + 手艺人 + 路程 共3个维度
        Dim = M + descition_num # 初始化Dim
        varTypes = np.array([0] * Dim) # 初始化varTypes（决策变量的类型，0：实数；1：整数）
        lb = [0] * Dim # 决策变量最小值
        ub = [1] * Dim # 决策变量最大值


        lbin = [1] * Dim # 决策变量是否大于等于最小值(0小于,1小于等于)
        ubin = [1] * Dim # 决策变量是否小于等于最大值(0大于,1大于等于)
        # 调用父类构造方法完成实例化
        ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)

    def aimFunc(self, pop): # 目标函数
        Vars = pop.Phen # 得到决策变量矩阵
        XM = Vars[:,(self.M-1):]
        g = 100 * (self.Dim - self.M + 1 + np.sum(((XM - 0.5)**2 - np.cos(20 * np.pi * (XM - 0.5))), 1, keepdims = True))
        ones_metrix = np.ones((Vars.shape[0], 1))
        f = 0.5 * np.fliplr(np.cumprod(np.hstack([ones_metrix, Vars[:,:self.M-1]]), 1)) * np.hstack([ones_metrix, 1 - Vars[:, range(self.M - 2, -1, -1)]]) * np.tile(1 + g, (1, self.M))
        pop.ObjV = f # 把求得的目标函数值赋值给种群pop的ObjV

    def calBest(self): # 计算全局最优解
        uniformPoint, ans = ea.crtup(self.M, 10000) # 生成10000个在各目标的单位维度上均匀分布的参考点
        globalBestObjV = uniformPoint / 2
        return globalBestObjV
